#include "XmlParser.h"
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/SAX.h>
#include <libxml/xmlstring.h>
#include <memory>
#include <stack>
#include <iostream>
#include <assert.h>
namespace tinkle{

////////////////////////////////////////////////////
// Implement of XmlParse
	



////////////////////////////////////////////////////////////////////////
	XmlParser::XmlParser() : _c_txt(NULL), _node(NULL){

		//set sax handler
		memset(&_sax_hdl, 0, sizeof(_sax_hdl));
		xmlSAX2InitDefaultSAXHandler(&_sax_hdl, 0);		
		xmlSAXVersion(&_sax_hdl, 1);
		_sax_hdl.startElement =	On_Start_Element;
		_sax_hdl.endElement = On_End_Element;
		_sax_hdl.characters = On_Characters;
		_sax_hdl.error = On_Error;
		_sax_hdl.warning = On_Warning;
		
		UnregisterCallbackFunc();
		
		Refresh(false);		
	}
	
	XmlParser::~XmlParser() {
		Refresh(false);
	}

	int XmlParser::Refresh(bool mode) { 
        
        //bad!!!!!
        _refreshnextime = true;

		_r_mode = mode;
		_badparser = false;

		//_clear();			

		return PARSER_NO_ERROR;
	}

	void XmlParser::_clear() {
		if (_c_txt) {
			xmlFreeParserCtxt(_c_txt);			
            _c_txt = NULL;
            //xmlCtxtReset(_c_txt);
		}
		if (_node) {
			//delete _node;
			_node = NULL;			
		}			
		_stack_clear();
		_depth = 0;
	}

	int XmlParser::Parse(const std::string& chunk) {	
		if (_badparser) return PARSER_BAD_PARSER;
        if (_refreshnextime) {
            _refreshnextime = false;
            _clear();
        }
		if (_c_txt == NULL) {
			//_c_txt = xmlCreatePushParserCtxt(&_sax_hdl, NULL, chunk.c_str(), chunk.length(), NULL);
			_c_txt = xmlCreatePushParserCtxt(&_sax_hdl, NULL, NULL, 0, NULL);
			if ( NULL != _c_txt)	_c_txt->_private = this;
			if ( NULL == _c_txt) return PARSER_CANT_CREATE;						 
		}
		
		//char buf1[100];
		//strcpy(buf1, chunk.c_str());
		//xmlParseChunk(_c_txt, buf1, strlen(buf1), 0);

		xmlParseChunk(_c_txt, chunk.c_str(), chunk.length(), 0);		
		//xmlParseChunk(_c_txt, "<p></p>", sizeof("<p></p>"), 0);
		

		return PARSER_NO_ERROR;
	}

	void XmlParser::_stack_clear() {
		//while (!_stack.empty()) _stack.pop();
		_sp = 0;
	}

/////////////////////////////////////////////////////////////////////
	void XmlParser::On_Characters(void *ctx, const xmlChar *ch, int len) {
		XmlParser* _p = (XmlParser*)((xmlParserCtxtPtr)ctx)->_private;
		_p->_curEle->SetContent(std::string(reinterpret_cast<const char*>(ch),  len));
	}

	void XmlParser::On_Start_Element(void * ctx, const xmlChar * name, const xmlChar ** atts){
//		std::cout << "Start: " << name << std::endl;
		XmlParser* _p = (XmlParser*)((xmlParserCtxtPtr)ctx)->_private;
		if (_p->_badparser) return;
		_p->_depth++;

		//stream node
		if (1 == _p->_depth || 2 == _p->_depth) {
			_p->_node = new XmlNode(_local_name(name));
			_p->_curEle = _p->_node->GetElement();
		}
		else {
			//_p->_stack.push(_p->_curEle);
			//_p->_stack[_p->_sp++] = _p->_curEle;
			ElementPtr tmp = _p->_curEle;
			_p->_curEle = tmp->AddChild(_local_name(name));
			_p->_stack_push(tmp);	
		}
		for (; atts && *atts ; atts += 2) {
			//namespace declaration
			if (_start_with_xmlns(*atts)) {			
				if (xmlStrlen(*atts) == 5) {				
					_p->_curEle->DeclNameSpace(std::string(reinterpret_cast<const char*>(*(atts+1))), std::string(""));
				}
				else {							
					_p->_curEle->DeclNameSpace(std::string(reinterpret_cast<const char*>(*(atts+1))), _local_name(*atts));
				}				
				continue;
		    }				
			_p->_curEle->SetAttrValue(reinterpret_cast<const char*>(*atts), reinterpret_cast<const char*>(*(atts+1)));
		}
		_p->_curEle->SetNameSpace(_prefix(name));
		if (1 == _p->_depth && _p->_notif) _p->_notif(_p->_node, _p->_notif_extra);		
	}
	void XmlParser::On_End_Element(void* ctx, const xmlChar* name) {
		XmlParser* _p = (XmlParser*)((xmlParserCtxtPtr)ctx)->_private;
		if (_p->_badparser) return;
		_p->_depth--;

		if (0 == _p->_depth) {
			return;
		}

		if (1 == _p->_depth && _p->_notif)  {
			 _p->_notif(_p->_node, _p->_notif_extra);
			 _p->_node = NULL;
		}
		else 	{			
			_p->_curEle = _p->_stack_pop();
		}
	}

	void XmlParser::On_Error(void* ctx, const char* fmt, ...) {
		XmlParser* _p = (XmlParser*)((xmlParserCtxtPtr)ctx)->_private;
		_p->_badparser =  true;
	}
	void XmlParser::On_Warning(void* ctx, const char* fmt, ...) {

	}

	std::string XmlParser::_prefix(const xmlChar* name) {		
		if (NULL == name) return "";
		std::string s(reinterpret_cast<const char *>(name));
		std::size_t found = s.find_last_of(':');
		if  (std::string::npos == found) return "";
		
		s.erase(found);
		return s;
	}

	std::string XmlParser::_local_name(const xmlChar* name) {		
		if (NULL == name) return "";
		std::string s(reinterpret_cast<const char*>(name));
		std::size_t found = s.find_last_of(':');
		if (std::string::npos == found) return s;

		s.erase(0, found+1);
		return s;
	}

	bool XmlParser::_start_with_xmlns(const xmlChar* s) {
		if (NULL == s) return false;
		return s[0] == 'x' 
			&& s[1]	== 'm'
			&& s[2] == 'l'
			&& s[3] == 'n'
			&& s[4] == 's';
	}

	ElementPtr XmlParser::_stack_pop() {assert(_sp > 0); return _stack[--_sp];}
	void XmlParser::_stack_push(ElementPtr &e) {_stack[_sp++] = e;}

/////////////////////////////////////////////////////////////////////

	
} 